# QueryMapper.java

    package org.codefilarete.stalactite.engine;

    import java.lang.reflect.Executable;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.function.BiConsumer;

    import org.codefilarete.reflection.MethodReferenceCapturer;
    import org.codefilarete.stalactite.engine.runtime.BeanPersister;
    import org.codefilarete.stalactite.query.builder.SQLBuilder;
    import org.codefilarete.stalactite.query.model.Selectable;
    import org.codefilarete.stalactite.sql.ConnectionProvider;
    import org.codefilarete.stalactite.sql.result.Accumulator;
    import org.codefilarete.stalactite.sql.result.Accumulators;
    import org.codefilarete.stalactite.sql.result.BeanRelationFixer;
    import org.codefilarete.stalactite.sql.result.MultipleColumnsReader;
    import org.codefilarete.stalactite.sql.result.ResultSetRowAssembler;
    import org.codefilarete.stalactite.sql.result.ResultSetRowTransformer;
    import org.codefilarete.stalactite.sql.result.SingleColumnReader;
    import org.codefilarete.stalactite.sql.result.WholeResultSetTransformer;
    import org.codefilarete.stalactite.sql.result.WholeResultSetTransformer.AssemblyPolicy;
    import org.codefilarete.stalactite.sql.statement.ReadOperation;
    import org.codefilarete.stalactite.sql.statement.ReadOperationFactory;
    import org.codefilarete.stalactite.sql.statement.StringParamedSQL;
    import org.codefilarete.stalactite.sql.statement.binder.ColumnBinderRegistry;
    import org.codefilarete.stalactite.sql.statement.binder.NullAwareParameterBinder;
    import org.codefilarete.stalactite.sql.statement.binder.ParameterBinder;
    import org.codefilarete.tool.Reflections;
    import org.codefilarete.tool.collection.Arrays;
    import org.codefilarete.tool.collection.Iterables;
    import org.codefilarete.tool.function.Converter;
    import org.codefilarete.tool.function.SerializableTriFunction;
    import org.danekja.java.util.function.serializable.SerializableBiConsumer;
    import org.danekja.java.util.function.serializable.SerializableBiFunction;
    import org.danekja.java.util.function.serializable.SerializableFunction;
    import org.danekja.java.util.function.serializable.SerializableSupplier;

    import static org.codefilarete.stalactite.sql.statement.binder.NullAwareParameterBinder.ALWAYS_SET_NULL_INSTANCE;
    import static org.codefilarete.tool.bean.Objects.preventNull;

    /**
     * A class aimed at querying the database and creating Java beans from it.
     * Beans resulting of it are not expected to be used as entity and given to {@link BeanPersister}, noticibly for insert
     * or update, because one should ensure that fields read from its query must be the same as those done by {@link BeanPersister}, otherwise it may
     * result in column value erasure.
     *
     * @author Guillaume Mary
     */
    public class QueryMapper<C> implements BeanKeyQueryMapper<C>, BeanPropertyQueryMapper<C> {

    	/** Default method capturer. Shared to cache result of each lookup. */
    	private static final MethodReferenceCapturer METHOD_REFERENCE_CAPTURER = new MethodReferenceCapturer();

    	/** The method capturer (when column types are not given by {@link #map(String, SerializableBiConsumer)}) */
    	private final MethodReferenceCapturer methodReferenceCapturer;

    	/** Type of bean that will be built by {@link #execute(ConnectionProvider)} */
    	private final Class<C> rootBeanType;

    	/** The sql provider */
    	private final SQLBuilder sqlBuilder;

    	/** The registry of {@link ParameterBinder}s, for column reading as well as sql argument setting */
    	private final ColumnBinderRegistry columnBinderRegistry;

    	/** {@link ParameterBinder}s per ({@link java.sql.PreparedStatement}) parameter */
    	private final Map<String, ParameterBinder<?>> sqlParameterBinders = new HashMap<>();

    	/** SQL argument values (for where clause, or anywhere else) */
    	private final Map<String, Object> sqlArguments = new HashMap<>();

    	/** Delegate for {@link java.sql.ResultSet} transformation, will get all the mapping configuration */
    	private WholeResultSetTransformer<C, ?> rootTransformer;

    	private final QueryMapping<C, ?> mapping = new QueryMapping<>();

    	private final ReadOperationFactory readOperationFactory;

    	private final Map<Selectable<?>, String> aliases = new HashMap<>();

    	/**
    	 * Simple constructor
    	 *
    	 * @param rootBeanType type of built bean
    	 * @param sqlBuilder the sql to execute
    	 * @param columnBinderRegistry a provider for SQL parameters and selected column
    	 */
    	public QueryMapper(Class<C> rootBeanType,
    					   CharSequence sqlBuilder,
    					   ColumnBinderRegistry columnBinderRegistry,
    					   ReadOperationFactory readOperationFactory) {
    		this(rootBeanType, sqlBuilder, columnBinderRegistry, METHOD_REFERENCE_CAPTURER, readOperationFactory);
    	}

    	/**
    	 * Simple constructor
    	 *
    	 * @param rootBeanType type of built bean
    	 * @param sqlBuilder the sql provider
    	 * @param columnBinderRegistry a provider for SQL parameters and selected column
    	 */
    	public QueryMapper(Class<C> rootBeanType,
    					   SQLBuilder sqlBuilder,
    					   ColumnBinderRegistry columnBinderRegistry,
    					   ReadOperationFactory readOperationFactory) {
    		this(rootBeanType, sqlBuilder, columnBinderRegistry, METHOD_REFERENCE_CAPTURER, readOperationFactory);
    	}

    	/**
    	 * Constructor to share {@link MethodReferenceCapturer} between instance of {@link QueryMapper}
    	 *
    	 * @param rootBeanType type of built bean
    	 * @param sqlBuilder the sql to execute
    	 * @param columnBinderRegistry a provider for SQL parameters and selected column
    	 * @param methodReferenceCapturer a method capturer (when column types are not given by {@link #map(String, SerializableBiConsumer)}),
    	 * default is {@link #METHOD_REFERENCE_CAPTURER}
    	 */
    	public QueryMapper(Class<C> rootBeanType,
    					   CharSequence sqlBuilder,
    					   ColumnBinderRegistry columnBinderRegistry,
    					   MethodReferenceCapturer methodReferenceCapturer,
    					   ReadOperationFactory readOperationFactory) {
    		this(rootBeanType, () -> sqlBuilder, columnBinderRegistry, methodReferenceCapturer, readOperationFactory);
    	}

    	/**
    	 * Constructor to share {@link MethodReferenceCapturer} between instance of {@link QueryMapper}
    	 *
    	 * @param rootBeanType type of built bean
    	 * @param sqlBuilder the sql provider
    	 * @param columnBinderRegistry a provider for SQL parameters and selected column
    	 * @param methodReferenceCapturer a method capturer (when column types are not given by {@link #map(String, SerializableBiConsumer)}),
    	 * default is {@link #METHOD_REFERENCE_CAPTURER}
    	 */
    	public QueryMapper(Class<C> rootBeanType,
    					   SQLBuilder sqlBuilder,
    					   ColumnBinderRegistry columnBinderRegistry,
    					   MethodReferenceCapturer methodReferenceCapturer,
    					   ReadOperationFactory readOperationFactory) {
    		this.rootBeanType = rootBeanType;
    		this.sqlBuilder = sqlBuilder;
    		this.columnBinderRegistry = columnBinderRegistry;
    		this.methodReferenceCapturer = methodReferenceCapturer;
    		this.readOperationFactory = readOperationFactory;
    	}

    	public void setColumnAliases(Map<Selectable<?>, String> aliases) {
    		this.aliases.putAll(aliases);
    	}

    	@Override
    	public QueryMapper<C> mapKey(SerializableSupplier<C> factory) {
    		this.rootTransformer = new WholeResultSetTransformer<>(rootBeanType, factory);
    		return this;
    	}

    	@Override
    	public <I> QueryMapper<C> mapKey(SerializableFunction<I, C> factory, String columnName) {
    		MethodReferenceCapturer methodReferenceCapturer = new MethodReferenceCapturer();
    		// Looking for column type (necessary to know how to read ResultSet) by looking to first argument of given function
    		// (which can be either a constructor or a method factory)
    		Executable executable = methodReferenceCapturer.findExecutable(factory);
    		this.rootTransformer = buildSingleColumnKeyTransformer(new ColumnDefinition<>(columnName, (Class<I>) executable.getParameterTypes()[0]), factory);
    		return this;
    	}

    	@Override
    	public <I, J> QueryMapper<C> mapKey(SerializableBiFunction<I, J, C> factory, String column1, String column2) {
    		MethodReferenceCapturer methodReferenceCapturer = new MethodReferenceCapturer();
    		// Looking for column type (necessary to know how to read ResultSet) by looking to first argument of given function
    		// (which can be either a constructor or a method factory)
    		Executable executable = methodReferenceCapturer.findExecutable(factory);
    		SerializableFunction<Object[], C> constructorInvokation = args -> (C) factory.apply((I) args[0], (J) args[1]);
    		this.rootTransformer = buildComposedKeyTransformer(Arrays.asSet(
    				new ColumnDefinition<>(column1, executable.getParameterTypes()[0]),
    				new ColumnDefinition<>(column2, executable.getParameterTypes()[1])),
    				constructorInvokation);
    		return this;
    	}

    	@Override
    	public <I, J, K> QueryMapper<C> mapKey(SerializableTriFunction<I, J, K, C> factory, String column1, String column2, String column3) {
    		MethodReferenceCapturer methodReferenceCapturer = new MethodReferenceCapturer();
    		Executable executable = methodReferenceCapturer.findExecutable(factory);
    		// Looking for column type (necessary to know how to read ResultSet) by looking to first argument of given function
    		// (which can be either a constructor or a method factory)
    		SerializableFunction<Object[], C> constructorInvokation = args -> (C) factory.apply((I) args[0], (J) args[1], (K) args[2]);
    		this.rootTransformer = buildComposedKeyTransformer(Arrays.asSet(
    				new ColumnDefinition<>(column1, executable.getParameterTypes()[0]),
    				new ColumnDefinition<>(column2, executable.getParameterTypes()[1]),
    				new ColumnDefinition<>(column3, executable.getParameterTypes()[2])),
    				constructorInvokation);
    		return this;
    	}

    	/**
    	 * Defines the key column and the way to create the bean : a constructor with the key as parameter.
    	 *
    	 * @param <I> the type of the column, which is also that of the factory argument
    	 * @param factory the factory function that will instantiate new beans (with key as single argument)
    	 * @param columnName the key column name
    	 * @param columnType the type of the column, which is also that of the factory argument
    	 * @return this
    	 */
    	@Override
    	public <I> QueryMapper<C> mapKey(SerializableFunction<I, C> factory, String columnName, Class<I> columnType) {
    		this.rootTransformer = buildSingleColumnKeyTransformer(new ColumnDefinition<>(columnName, columnType), factory);
    		return this;
    	}

    	/**
    	 * Defines key columns and the way to create the bean : a constructor with 2 arguments.
    	 *
    	 * @param <I> the type of the column, which is also that of the factory argument
    	 * @param factory the factory function that will instantiate new beans (with key as single argument)
    	 * @param column1Name the first key column name
    	 * @param column1Type the type of the first column, which is also that of the factory first argument
    	 * @param column2Name the second key column name
    	 * @param column2Type the type of the second column, which is also that of the factory second argument
    	 * @return this
    	 */
    	@Override
    	public <I, J> QueryMapper<C> mapKey(SerializableBiFunction<I, J, C> factory, String column1Name, Class<I> column1Type,
    										String column2Name, Class<J> column2Type) {
    		SerializableFunction<Object[], C> constructorInvokation = args -> (C) factory.apply((I) args[0], (J) args[1]);
    		this.rootTransformer = buildComposedKeyTransformer(Arrays.asSet(
    				new ColumnDefinition<>(column1Name, column1Type),
    				new ColumnDefinition<>(column2Name, column2Type)),
    				constructorInvokation);
    		return this;
    	}

    	/**
    	 * Defines key columns and the way to create the bean : a constructor with 3 arguments.
    	 *
    	 * @param <I> the type of the column, which is also that of the factory argument
    	 * @param factory the factory function that will instantiate new beans (with key as single argument)
    	 * @param column1Name the first key column name
    	 * @param column1Type the type of the first column, which is also that of the factory first argument
    	 * @param column2Name the second key column name
    	 * @param column2Type the type of the second column, which is also that of the factory second argument
    	 * @param column3Name the third key column name
    	 * @param column3Type the type of the third column, which is also that of the factory third argument
    	 * @return this
    	 */
    	@Override
    	public <I, J, K> QueryMapper<C> mapKey(SerializableTriFunction<I, J, K, C> factory, String column1Name, Class<I> column1Type,
    										   String column2Name, Class<J> column2Type,
    										   String column3Name, Class<K> column3Type) {
    		SerializableFunction<Object[], C> constructorInvokation = args -> (C) factory.apply((I) args[0], (J) args[1], (K) args[2]);
    		this.rootTransformer = buildComposedKeyTransformer(Arrays.asSet(
    				new ColumnDefinition<>(column1Name, column1Type),
    				new ColumnDefinition<>(column2Name, column2Type),
    				new ColumnDefinition<>(column3Name, column3Type)),
    				constructorInvokation);
    		return this;
    	}

    	/**
    	 * Same as {@link #mapKey(SerializableFunction, String, Class)} but with {@link Selectable} signature
    	 *
    	 * @param <I> type of the key
    	 * @param factory the factory function that will instantiate new beans (with key as single argument)
    	 * @param column the mapped column used as a key
    	 * @return this
    	 */
    	@Override
    	public <I> QueryMapper<C> mapKey(SerializableFunction<I, C> factory,
    									 Selectable<I> column) {
    		this.rootTransformer = buildSingleColumnKeyTransformer(new ColumnWrapper<>(column), factory);
    		return this;
    	}

    	/**
    	 * Same as {@link #mapKey(SerializableFunction, Selectable)} with a 2-args constructor
    	 *
    	 * @param <I> type of the key
    	 * @param factory the factory function that will instantiate new beans (with key as single argument)
    	 * @param column1 the first column of the key
    	 * @param column2 the second column of the key
    	 * @return this
    	 */
    	@Override
    	public <I, J> QueryMapper<C> mapKey(SerializableBiFunction<I, J, C> factory,
    										Selectable<I> column1,
    										Selectable<J> column2
    	) {
    		SerializableFunction<Object[], C> constructorInvokation = args -> (C) factory.apply((I) args[0], (J) args[1]);
    		this.rootTransformer = buildComposedKeyTransformer(Arrays.asSet(
    				new ColumnWrapper<>(column1),
    				new ColumnWrapper<>(column2)),
    				constructorInvokation);
    		return this;
    	}

    	/**
    	 * Same as {@link #mapKey(SerializableFunction, Selectable)} with a 3-args constructor
    	 *
    	 * @param <I> type of the key
    	 * @param factory the factory function that will instantiate new beans (with key as single argument)
    	 * @param column1 the first column of the key
    	 * @param column2 the second column of the key
    	 * @param column3 the third column of the key
    	 * @return this
    	 */
    	@Override
    	public <I, J, K> QueryMapper<C> mapKey(SerializableTriFunction<I, J, K, C> factory,
    										   Selectable<I> column1,
    										   Selectable<J> column2,
    										   Selectable<K> column3
    	) {
    		SerializableFunction<Object[], C> constructorInvokation = args -> (C) factory.apply((I) args[0], (J) args[1], (K) args[2]);
    		this.rootTransformer = buildComposedKeyTransformer(Arrays.asSet(
    				new ColumnWrapper<>(column1),
    				new ColumnWrapper<>(column2),
    				new ColumnWrapper<>(column3)),
    				constructorInvokation);
    		return this;
    	}

    	@Override
    	public <I> QueryMapper<I> mapKey(String columnName, Class<I> columnType) {
    		this.rootTransformer = (WholeResultSetTransformer<C, ?>) buildSingleColumnKeyTransformer(new ColumnDefinition<>(columnName, columnType), SerializableFunction.identity());
    		return (QueryMapper<I>) this;
    	}

    	/**
    	 * Defines a mapping between a column of the query and a bean property through its setter
    	 *
    	 * @param columnName a column name
    	 * @param setter the setter function
    	 * @param columnType the type of the column, which is also that of the setter argument
    	 * @param <I> the type of the column, which is also that of the setter argument
    	 * @return this
    	 */
    	@Override
    	public <I> QueryMapper<C> map(String columnName, BiConsumer<C, I> setter, Class<I> columnType) {
    		map(new ColumnMapping<>(columnName, setter, columnType));
    		return this;
    	}

    	/**
    	 * Same as {@link #map(String, BiConsumer, Class)}.
    	 * Differs by providing the possiblity to convert the value before setting it onto the bean.
    	 *
    	 * @param columnName a column name
    	 * @param setter the setter function
    	 * @param columnType the type of the column, which is also that of the setter argument
    	 * @param converter a converter of the read value from ResultSet
    	 * @param <I> the type of the setter argument, which is also that of the converter result
    	 * @param <J> the type of the column, which is also that of the converter argument
    	 * @return this
    	 */
    	@Override
    	public <I, J> QueryMapper<C> map(String columnName, SerializableBiConsumer<C, J> setter, Class<I> columnType, Converter<I, J> converter) {
    		return map(columnName, (c, i) -> setter.accept(c, converter.convert(i)), columnType);
    	}

    	/**
    	 * Defines a mapping between a column of the query and a bean property through its setter.
    	 * WARNING : Column type will be deduced from the setter type. To do it, some bytecode enhancement is required, therefore it's not the cleanest
    	 * way to define the binding. Prefer {@link #map(String, BiConsumer, Class)}
    	 *
    	 * @param columnName a column name
    	 * @param setter the setter function
    	 * @param <I> the type of the column, which is also that of the setter argument
    	 * @return this
    	 */
    	@Override
    	public <I> QueryMapper<C> map(String columnName, SerializableBiConsumer<C, I> setter) {
    		map(columnName, setter, giveColumnType(setter));
    		return this;
    	}

    	/**
    	 * Same as {@link #map(String, SerializableBiConsumer)}.
    	 * Differs by providing the possibility to convert the value before setting it onto the bean.
    	 *
    	 * @param columnName a column name
    	 * @param setter the setter function
    	 * @param converter a converter of the read value from ResultSet
    	 * @param <I> the type of the setter argument, which is also that of the converter result
    	 * @return this
    	 */
    	@Override
    	public <I, J> QueryMapper<C> map(String columnName, SerializableBiConsumer<C, J> setter, Converter<I, J> converter) {
    		Method method = methodReferenceCapturer.findMethod(setter);
    		Class<I> aClass = (Class<I>) method.getParameterTypes()[0];
    		return map(columnName, (c, i) -> setter.accept(c, converter.convert(i)), aClass);
    	}

    	/**
    	 * Same as {@link #map(String, BiConsumer, Class)} but with {@link Selectable} signature
    	 *
    	 * @param column the mapped column
    	 * @param setter the setter function
    	 * @param <I> the type of the column, which is also that of the setter argument
    	 * @return this
    	 */
    	@Override
    	public <I> QueryMapper<C> map(Selectable<I> column, BiConsumer<C, I> setter) {
    		map(new ColumnMapping<>(column, setter));
    		return this;
    	}

    	/**
    	 * Same as {@link #map(Selectable, BiConsumer)}.
    	 * Differs by providing the possibility to convert the value before setting it onto the bean.
    	 *
    	 * @param column the mapped column
    	 * @param setter the setter function
    	 * @param converter a converter of the read value from ResultSet
    	 * @param <I> the type of the setter argument, which is also that of the converter result
    	 * @param <J> the type of the column, which is also that of the converter argument
    	 * @return this
    	 */
    	@Override
    	public <I, J> QueryMapper<C> map(Selectable<I> column,
    									 BiConsumer<C, J> setter,
    									 Converter<I, J> converter) {
    		return map(column, (c, i) -> setter.accept(c, converter.convert(i)));
    	}

    	private <I> void map(ColumnMapping<C, I> columnMapping) {
    		mapping.add((ColumnMapping) columnMapping);
    	}

    	/** Overridden to adapt return type */
    	@Override
    	public QueryMapper<C> map(ResultSetRowAssembler<C> assembler) {
    		return map(assembler, AssemblyPolicy.ON_EACH_ROW);
    	}

    	@Override
    	public QueryMapper<C> map(ResultSetRowAssembler<C> assembler, AssemblyPolicy assemblyPolicy) {
    		mapping.add(assembler, assemblyPolicy);
    		return this;
    	}

    	@Override
    	public <K, V> QueryMapper<C> map(BeanRelationFixer<C, V> combiner, ResultSetRowTransformer<V, K> relatedBeanCreator) {
    		mapping.add((BeanRelationFixer) combiner, relatedBeanCreator);
    		return this;
    	}

    	/**
    	 * Executes the query onto the connection given by the {@link ConnectionProvider}. Transforms the result to a list of beans thanks to the
    	 * definition given through {@link #mapKey(SerializableFunction, String, Class)}, {@link #map(String, BiConsumer, Class)}
    	 * and {@link #map(String, SerializableBiConsumer)} methods.
    	 *
    	 * @param connectionProvider the object that will give the {@link java.sql.Connection}
    	 * @return a {@link Set} filled by the instances built
    	 */
    	public Set<C> execute(ConnectionProvider connectionProvider) {
    		return execute(connectionProvider, Accumulators.toSet());
    	}

    	public <R> R execute(ConnectionProvider connectionProvider, Accumulator<C, ?, R> accumulator) {
    		WholeResultSetTransformer<C, ?> transformerToUse;
    		if (rootTransformer == null) {
    			// No key defined (none of mapKey(..) methods were invoked), therefore we'll use default (no-arg) constructor
    			transformerToUse = new WholeResultSetTransformer<>(rootBeanType, () -> Reflections.newInstance(rootBeanType));
    		} else {
    			transformerToUse = rootTransformer;
    		}
    		this.mapping.applyTo((WholeResultSetTransformer) transformerToUse);
    		StringParamedSQL parameterizedSQL = new StringParamedSQL(this.sqlBuilder.toSQL().toString(), sqlParameterBinders);
    		parameterizedSQL.setValues(sqlArguments);
    		try (ReadOperation<String> readOperation = readOperationFactory.createInstance(parameterizedSQL, connectionProvider)) {
    			return transformerToUse.transformAll(readOperation.execute(), accumulator);
    		}
    	}

    	private <I, O> WholeResultSetTransformer<O, I> buildSingleColumnKeyTransformer(Column<I> keyColumn, SerializableFunction<I, O> beanFactory) {
    		return new WholeResultSetTransformer<>((Class<O>) rootBeanType, keyColumn.getName(), keyColumn.getBinder(), beanFactory);
    	}

    	private WholeResultSetTransformer<C, Object[]> buildComposedKeyTransformer(Set<Column> columns, SerializableFunction<Object[], C> beanFactory) {
    		Map<Column<?>, String> computedAliases = new HashMap<>();
    		Set<SingleColumnReader> columnReaders = Iterables.collect(columns, c -> {
    			ParameterBinder reader = c.getBinder();
    			String alias;
    			if (c instanceof ColumnWrapper) {
    				alias = preventNull(aliases.get(((ColumnWrapper<?>) c).getColumn()), c.getName());
    			} else {
    				alias = c.getName();
    			}
    			computedAliases.put(c, alias);
    			return new SingleColumnReader<>(alias, reader);
    		}, HashSet::new);
    		MultipleColumnsReader<Object[]> multipleColumnsReader = new MultipleColumnsReader<>(columnReaders, resultSetRow -> {
    			// we transform all columns value into a Object[]
    			Object[] constructorArgs = new Object[columns.size()];
    			int i = 0;
    			for (Column column : columns) {
    				constructorArgs[i++] = resultSetRow.get(computedAliases.get(column));
    			}
    			return constructorArgs;
    		});

    		ResultSetRowTransformer<C, Object[]> resultSetRowConverter = new ResultSetRowTransformer<>(
    				rootBeanType,
    				multipleColumnsReader,
    				beanFactory);
    		return new WholeResultSetTransformer<>(resultSetRowConverter);
    	}

    	private <I> Class<I> giveColumnType(SerializableBiConsumer<C, I> setter) {
    		Method method = methodReferenceCapturer.findMethod(setter);
    		// we could take the first parameter type, but with a particular syntax of setter it's insufficient, last element is better
    		return (Class<I>) Arrays.last(method.getParameterTypes());
    	}

    	/**
    	 * Sets a value for a SQL parameter. Not to be used with Collection/Iterable value : see {@link #set(String, Iterable, Class)} dedicated method for it.
    	 * No check for "already set" argument is done, so one can overwrite/redefine an existing value. This lets one reexecutes a
    	 * {@link QueryMapper} with different parameters.
    	 *
    	 * @param paramName the name of the SQL parameter to be set
    	 * @param value the value of the parameter, null is authorized but since type can't be know in this case {@link java.sql.PreparedStatement#setObject(int, Object)}
    	 * 				will be used, so prefer {@link #set(String, Object, Class)} if your database driver doesn't support well setObject(..),
    	 * 			    see	{@link NullAwareParameterBinder#ALWAYS_SET_NULL_INSTANCE}
    	 * @return this
    	 * @see #set(String, Iterable, Class)
    	 */
    	public QueryMapper<C> set(String paramName, Object value) {
    		return set(paramName, value, value == null ? null : (Class) value.getClass());
    	}

    	/**
    	 * Sets a value for a SQL parameter. Not for Collection/Iterable value : see {@link #set(String, Iterable, Class)} dedicated method for it.
    	 * No check for "already set" argument is done, so one can overwrite/redefine an existing value. This lets one reexecutes a
    	 * {@link QueryMapper} with different parameters.
    	 *
    	 * @param paramName the name of the SQL parameter to be set
    	 * @param value the value of the parameter
    	 * @param valueType the content type of the {@link Iterable}, more exactly will determine which {@link ParameterBinder} to be used
    	 * @return this
    	 * @see #set(String, Iterable, Class)
    	 */
    	public <O> QueryMapper<C> set(String paramName, O value, Class<? super O> valueType) {
    		ParameterBinder<?> parameterBinder;
    		if (value == null) {
    			parameterBinder = ALWAYS_SET_NULL_INSTANCE;
    		} else {
    			if (value.getClass().isArray()) {
    				valueType = (Class<? super O>) Iterables.first((Object[]) value);
    			} else if (Iterable.class.isAssignableFrom(value.getClass())) {
    				valueType = (Class<? super O>) Iterables.first((Iterable) value).getClass();
    			}
    			parameterBinder = columnBinderRegistry.getBinder(valueType);
    		}
    		this.sqlParameterBinders.put(paramName, parameterBinder);
    		this.sqlArguments.put(paramName, value);
    		return this;
    	}

    	/**
    	 * Sets a value for a Collection/Iterable SQL argument. Must be distinguished from {@link #set(String, Object)} because real {@link Iterable}
    	 * content type guessing can be difficult (or at least not accurate) and can lead to {@link Iterable} consumption.
    	 * No check for "already set" argument is done, so one can overwrite/redefine an existing value. This lets one reexecutes a
    	 * {@link QueryMapper} with different parameters.
    	 *
    	 * @param paramName the name of the SQL parameter to be set
    	 * @param value the value of the parameter
    	 * @param valueType the content type of the {@link Iterable}, more exactly will determine which {@link ParameterBinder} to be used
    	 * @return this
    	 */
    	public <O> QueryMapper<C> set(String paramName, Iterable<O> value, Class<? super O> valueType) {
    		this.sqlParameterBinders.put(paramName, value == null ? ALWAYS_SET_NULL_INSTANCE : columnBinderRegistry.getBinder(valueType));
    		this.sqlArguments.put(paramName, value);
    		return this;
    	}

    	/**
    	 * An internal definition of a "column" : a selected column or a statement parameter
    	 * @param <T> the value type of the "column"
    	 */
    	private interface Column<T> {

    		String getName();

    		ParameterBinder<T> getBinder();
    	}

    	private class ColumnDefinition<T> implements QueryMapper.Column<T> {

    		private final String name;

    		private final Class<T> valueType;

    		private ColumnDefinition(String name, Class<T> valueType) {
    			this.name = name;
    			this.valueType = valueType;
    		}

    		public String getName() {
    			return name;
    		}

    		public ParameterBinder<T> getBinder() {
    			return columnBinderRegistry.getBinder(valueType);
    		}
    	}

    	private class ColumnWrapper<T> implements QueryMapper.Column<T> {

    		private final Selectable<T> column;

    		private ColumnWrapper(Selectable<T> column) {
    			this.column = column;
    		}

    		public String getName() {
    			return column.getExpression();
    		}

    		public ParameterBinder<T> getBinder() {
    			if (column instanceof org.codefilarete.stalactite.sql.ddl.structure.Column) {
    				return columnBinderRegistry.getBinder((org.codefilarete.stalactite.sql.ddl.structure.Column<?, T>) column);
    			} else {
    				return columnBinderRegistry.getBinder(column.getJavaType());
    			}
    		}

    		public Selectable<T> getColumn() {
    			return column;
    		}
    	}

    	/**
    	 * An internal class defining the way to map a result column to a bean "property" (more precisely a setter or whatever which takes the value as input)
    	 *
    	 * @param <T> the bean type that supports the setter
    	 * @param <I> the column value type which is also the input type of the property setter
    	 */
    	private class ColumnMapping<T, I> {

    		private final QueryMapper.Column<I> column;

    		private final BiConsumer<T, I> setter;

    		public ColumnMapping(String columnName, BiConsumer<T, I> setter, Class<I> columnType) {
    			this.column = new ColumnDefinition<>(columnName, columnType);
    			this.setter = setter;
    		}

    		public ColumnMapping(Selectable<I> column, BiConsumer<T, I> setter) {
    			this.column = new ColumnWrapper<>(column);
    			this.setter = setter;
    		}

    		public QueryMapper.Column<I> getColumn() {
    			return column;
    		}

    		public BiConsumer<T, I> getSetter() {
    			return setter;
    		}
    	}

    	/**
    	 * Stores configuration defined by user to let user uses {@link QueryMapper} methods in any order : without it mapKey(..) should be used first
    	 * because we might have to apply mapping to {@link WholeResultSetTransformer} instance
    	 *
    	 * @param <C> created instance type
    	 * @param <I> identifier type
    	 */
    	private static class QueryMapping<C, I> {

    		private final List<Mapping> mappings = new ArrayList<>(10);

    		public void add(QueryMapper<C>.ColumnMapping<C, I> columnMapping) {
    			mappings.add(new ColumnMapping(columnMapping));
    		}

    		public void add(ResultSetRowAssembler<C> assembler, AssemblyPolicy assemblyPolicy) {
    			mappings.add(new AssemblerMapping(assembler, assemblyPolicy));
    		}

    		public <K, V> void add(BeanRelationFixer<K, V> combiner, ResultSetRowTransformer<V, K> relatedBeanCreator) {
    			mappings.add(new RelationMapping(combiner, relatedBeanCreator));
    		}

    		/**
    		 * Transfer this mapping to given instance
    		 * @param target instance that will consume current mapping
    		 */
    		public void applyTo(WholeResultSetTransformer<C, I> target) {
    			this.mappings.forEach(m -> {
    				if (m instanceof ColumnMapping) {
    					QueryMapper.ColumnMapping columnMapping = ((ColumnMapping) m).columnMapping;
    					target.add(columnMapping.getColumn().getName(), columnMapping.getColumn().getBinder(), columnMapping.getSetter());
    				} else if (m instanceof AssemblerMapping) {
    					AssemblerMapping assemblerMapping = (AssemblerMapping) m;
    					target.add(assemblerMapping.assembler, assemblerMapping.policy);
    				} else if (m instanceof RelationMapping) {
    					RelationMapping relationMapping = (RelationMapping) m;
    					target.add((BeanRelationFixer<C, Object>) relationMapping.combiner::apply, relationMapping.relatedBeanCreator);
    				}
    			});
    		}

    		/**
    		 * A marking interface for different kind of mapping configurations, made to allow them being stored in {@link java.util.Collection}.
    		 * Only used by {@link QueryMapping}
    		 */
    		private interface Mapping {

    		}

    		/** Simple store for property-column mapping */
    		private static class ColumnMapping implements Mapping {
    			private final QueryMapper.ColumnMapping columnMapping;

    			private ColumnMapping(QueryMapper.ColumnMapping columnMapping) {
    				this.columnMapping = columnMapping;
    			}
    		}

    		/** Simple store for assembly mapping */
    		private static class AssemblerMapping implements Mapping {
    			private final ResultSetRowAssembler assembler;
    			private final AssemblyPolicy policy;

    			private AssemblerMapping(ResultSetRowAssembler assembler, AssemblyPolicy policy) {
    				this.assembler = assembler;
    				this.policy = policy;
    			}
    		}

    		/** Simple store for relation mapping */
    		private static class RelationMapping implements Mapping {
    			private final BeanRelationFixer combiner;
    			private final ResultSetRowTransformer relatedBeanCreator;

    			private RelationMapping(BeanRelationFixer combiner, ResultSetRowTransformer relatedBeanCreator) {
    				this.combiner = combiner;
    				this.relatedBeanCreator = relatedBeanCreator;
    			}
    		}
    	}
    }